| Custom Attributes > Garbage Collection > Class Helpers |
|
The Common Language Runtime
is a garbage-collected environment. This means the programmer is freed (for the
most part) from worrying about memory allocation. Broadly speaking, after you
allocate memory, the CLR determines when it is safe to free that memory.
"Safe to free" means that no more references to that memory exist.
Every class in the .NET
Framework (including Delphi for Microsoftâ .NET classes) inherits a method called Finalize. The
garbage collector calls the Finalize method when the memory for the object is
about to be freed. There are many potential problems with this arrangement, not
the least of which being that you have no control over when the Finalize method
is called. The asynchronous nature of finalization is a problem for objects
that open resources such as file handles and database connections, because the
Finalize method might not be called for some time, leaving these connections
open.
Another way to free up
resources is to implement what is known as the dispose pattern. Classes
adhering to the dispose pattern must implement the .NET interface called IDisposable.
IDisposable contains only one method, called Dispose. Unlike the
Finalize method, the Dispose method is public. It can be called directly by a user
of the class, as opposed to relying on the garbage collector to call it. This
gives you back control of freeing resources, but calling Dispose still does not
reclaim memory for the object itself - this is still under the pervue of the
garbage collector. Note that some classes in the .NET Framework implement both
Dispose, and another method such as Close. Typically the Close method simply
calls Dispose, but the extra method is provided because it seems more
"natural" for certain classes such as files.
Delphi for .NET classes are
free to use the Finalize method for freeing system resources, however the
recommended method is to implement the dispose pattern. The Delphi for .NET
compiler makes life even easier, by recognizing a very specific destructor
pattern in your class, and silently implementing the IDisposable
interface for you. This enables you to continue writing new code the same way
you always have, while allowing much of your existing Delphi code to run in the
garbage collected world of the CLR.
The compiler recognizes the
specific pattern of a Delphi destructor:
TMyClass = class(TObject)
destructor
Destroy; override;
end;
Your destructor must
fit this pattern exactly:
When the compiler recognizes
a destructor that fits the pattern, it silently implements the IDisposable
interface for you. The Free method is written so that if the class implements
the IDisposable interface (which it does), then the Dispose method is
called, which in turn calls your destructor.
You can still implement the IDisposable
interface directly, if you choose. However, the compiler's automatic
implementation of the Free-Dispose-Destroy mechanism cannot coexist with your
implementation of IDisposable. The two methods are mutually exclusive.
You must choose to either implement IDisposable directly, or equip your
class with the familiar destructor Destroy; override pattern and rely on the
compiler to do the rest. The Free method will call Dispose in either case, but
if you implement Dispose yourself, you must call your destructor yourself. If
you want to implement IDisposable yourself, your destructor cannot be
called Destroy.
Reference counting, as a
technique for memory management, is deprecated in the .NET Framework. The CLR
handles all memory allocation for you. In this preview release of the Delphi
for .NET compiler, the TInterfacedObject class is an alias for TObject. The
AddRef, Release, and QueryInterface methods do not exist.
If you have existing Delphi
code that relies on reference counting to call your destructor, you will have
to reimplement this code when porting to the .NET environment. The reason for
this is that external consumers of your component (written in languages other
than Delphi) will not be calling AddRef and Release like they used to. Instead,
they will be relying on the garbage collector. The recommended approach is to
use the destructor Destroy; override pattern described above.
Unit initialization and
finalization continue to work in Delphi for .NET programs, though there are
some migration issues to be aware of.
Units that you depend on
will be initialized prior to initializing your own unit. However, there is no
way to guarantee the order in which units are initialized, nor is there a way
to guarantee when they will be initialized. Be aware of initialization code
that depends on another unit's initialization side effects, such as the
creation of a file. Such a dependency cannot be made to work reliably under
.NET.
Unit finalization continues
to exist as a language feature, but it is subject to the same constraints and
difficulties as the Finalize method of objects. These are, that unit
finalization is asynchronous, and, there no way to determine when it will
happen.
The main points to keep in
mind for garbage collection, and unit initialization and finalization are:
| Top | Contents | Copyrights | Borland Developer Network | Borland Home |